package com.own.stu.apm.agent.core.plugin.interceptor.enhence;

import com.own.stu.apm.agent.core.plugin.loader.InterceptorInstanceLoader;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.implementation.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

@Slf4j
public class InstanceMethodIntercept {

    InstanceMethodsAroundInterceptor instanceMethodsAroundInterceptor = null;

    public InstanceMethodIntercept(String methodInterceptor, ClassLoader classLoader) {
        try {
            InterceptorInstanceLoader.load(methodInterceptor, classLoader);
        } catch (Exception e) {
            log.error("load InstanceMethodIntercept :{} fail ", methodInterceptor, e);
        }
    }

    @RuntimeType
    public Object intercept(
            @Origin Object objIns,
            @Origin Method method,
            @AllArguments Object[] allMethodArgs,
            @SuperCall Callable<?> zCall
    ) throws Throwable {
        EnhenceInstanceField instance = (EnhenceInstanceField) objIns;
        try {
            instanceMethodsAroundInterceptor.beforeMethod(instance, method, allMethodArgs, method.getParameterTypes());
        } catch (Exception e) {
            log.error("class {} before exec instance method {} intercept fail", instance, method.getName(), e);
        }

        Object result = null;
        try {
            result = zCall.call();
        } catch (Throwable e) {
            try {
                instanceMethodsAroundInterceptor.handleEx(instance, method, allMethodArgs, method.getParameterTypes(), e);
            } catch (Throwable t) {
                log.error("class {} handle instance method {} fail", instance, method.getName(), t);
            }
            throw e;
        } finally {
            try {
                result = instanceMethodsAroundInterceptor.afterMethod(instance, method, allMethodArgs, method.getParameterTypes(), result);
            } catch (Throwable t) {
                log.error("class {} after exec instance method {} intercept fail", instance, method.getName(), t);
            }
        }
        return result;
    }
}
